﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using GenericGA.extrandom;

namespace GenericGA.generationmaker.selection
{
    /// <summary>
    /// A class which selects individuals by giving each individual a probability of being selected which is proportional to it's fitness. Cannot be used when individuals may have negative fitnessesCache.
    /// </summary>
    /// <typeparam name="P">The phenotype type, or type of the candidate solutions.</typeparam>
    public class FitnessProportionateSelection<P> : Selection<P>
    {

        /// <summary>
        /// A random number generator.
        /// </summary>
        private readonly ExtRandom randGen;

        /// <summary>
        /// The population from which to select.
        /// </summary>
        private List<Individual<P>> population;

        /// <summary>
        /// The total fitness of the population to select from.
        /// </summary>
        private double totalFitness;

        /// <summary>
        /// Create a fitness proportionate selecter.
        /// </summary>
        /// <param name="randGen">A random number generator.</param>
        public FitnessProportionateSelection(ExtRandom randGen)
        {
            this.randGen = randGen;
        }

        /// <summary>
        /// Set the population to select from.
        /// </summary>
        /// <param name="population">The population from which to select.</param>
        /// <param name="fitnessFunction">The fitness function to be used</param>
        public override void InitSelection(List<Individual<P>> population, GA<P>.FitnessFunctionType fitnessFunction)
        {
            this.population = population;
            
            totalFitness = 0.0;
            foreach (Individual<P> ind in population)
            {
                totalFitness += ind.CachedFitness;
            }
        }

        /// <summary>
        /// Select an individual.
        /// </summary>
        /// <returns>An individual from the population.</returns>
        public override Individual<P> Select()
        {
            double r = randGen.NextDouble() * totalFitness;

            double tmp = 0;
            foreach (Individual<P> ind in population)
            {
                tmp += ind.CachedFitness;
                if (tmp > r)
                {
                    return ind;
                }
            }

            return null;
        }

    }
}
